use crate::chess::*;

// 棋盘
#[derive(Copy, Clone)]
pub struct Board {
    pub pieces: [Option<(Color, Piece)>; 90],
}

impl Board {
    // 初始化棋盘
    pub fn new() -> Board {
        let mut pieces = [None; 90];
        // black piece
        pieces[0] = Some((Color::Black, Piece::Rook));
        pieces[1] = Some((Color::Black, Piece::Horse));
        pieces[2] = Some((Color::Black, Piece::Elephant));
        pieces[3] = Some((Color::Black, Piece::Advisor));
        pieces[4] = Some((Color::Black, Piece::General));
        pieces[5] = Some((Color::Black, Piece::Advisor));
        pieces[6] = Some((Color::Black, Piece::Elephant));
        pieces[7] = Some((Color::Black, Piece::Horse));
        pieces[8] = Some((Color::Black, Piece::Rook));
        pieces[19] = Some((Color::Black, Piece::Cannon));
        pieces[25] = Some((Color::Black, Piece::Cannon));
        pieces[27] = Some((Color::Black, Piece::Pawn));
        pieces[29] = Some((Color::Black, Piece::Pawn));
        pieces[31] = Some((Color::Black, Piece::Pawn));
        pieces[33] = Some((Color::Black, Piece::Pawn));
        pieces[35] = Some((Color::Black, Piece::Pawn));
        // red pieces
        pieces[89] = Some((Color::Red, Piece::Rook));
        pieces[88] = Some((Color::Red, Piece::Horse));
        pieces[87] = Some((Color::Red, Piece::Elephant));
        pieces[86] = Some((Color::Red, Piece::Advisor));
        pieces[85] = Some((Color::Red, Piece::General));
        pieces[84] = Some((Color::Red, Piece::Advisor));
        pieces[83] = Some((Color::Red, Piece::Elephant));
        pieces[82] = Some((Color::Red, Piece::Horse));
        pieces[81] = Some((Color::Red, Piece::Rook));
        pieces[70] = Some((Color::Red, Piece::Cannon));
        pieces[64] = Some((Color::Red, Piece::Cannon));
        pieces[62] = Some((Color::Red, Piece::Pawn));
        pieces[60] = Some((Color::Red, Piece::Pawn));
        pieces[58] = Some((Color::Red, Piece::Pawn));
        pieces[56] = Some((Color::Red, Piece::Pawn));
        pieces[54] = Some((Color::Red, Piece::Pawn));
        Board { pieces }
    }

    pub fn with_pieces(initial_pieces: &[(Pos, Color, Piece)]) -> Self {
        let mut pieces = [None; 90];
        for (pos, color, piece) in initial_pieces {
            pieces[pos.index()] = Some((*color, *piece));
        }
        Board { pieces }
    }

    pub fn get_piece(&self, pos: Pos) -> Option<(Color, Piece)> {
        self.pieces[pos.index()]
    }

    pub fn iter(&self) -> impl Iterator<Item = (Pos, Color, Piece)> + '_ {
        self.pieces
            .iter()
            .enumerate()
            .filter_map(|(pos, item)| item.map(|(color, piece)| (Pos(pos as u8), color, piece)))
    }

    // 走棋，返回被吃掉的棋子
    pub fn apply_move(&mut self, from: Pos, to: Pos) -> Option<(Color, Piece)> {
        let from = from.index();
        let to = to.index();
        let eaten_piece = self.pieces[to].take();
        self.pieces[to] = self.pieces[from].take();
        eaten_piece
    }

    // 撤回一步
    pub fn revert_move(&mut self, from: Pos, to: Pos, eaten_piece: Option<(Color, Piece)>) {
        let from = from.index();
        let to = to.index();
        self.pieces[from] = self.pieces[to].take();
        self.pieces[to] = eaten_piece;
    }

    // 可能的走法
    pub fn possible_moves(&self, from: Pos) -> Vec<Pos> {
        let mut possible = Vec::new();
        if let Some((color, piece)) = self.get_piece(from) {
            let (row, col) = from.row_col();
            for destination in match piece {
                Piece::General => {
                    [
                        (row + 1, col),
                        (row - 1, col),
                        (row, col + 1),
                        (row, col - 1),
                    ]
                    .into_iter()
                    // 不出界
                    .filter_map(Pos::from_row_col)
                    // 不出宫
                    .filter(|Pos(pos)| {
                        match color {
                            Color::Red => [66, 67, 68, 75, 76, 77, 84, 85, 86],
                            Color::Black => [3, 4, 5, 12, 13, 14, 21, 22, 23],
                        }
                        .contains(pos)
                    })
                    .collect::<Vec<_>>()
                }
                Piece::Advisor => {
                    [
                        (row + 1, col + 1),
                        (row + 1, col - 1),
                        (row - 1, col + 1),
                        (row - 1, col - 1),
                    ]
                    .into_iter()
                    // 不出界
                    .filter_map(Pos::from_row_col)
                    // 不出宫
                    .filter(|Pos(pos)| {
                        match color {
                            Color::Red => [66, 68, 76, 84, 86],
                            Color::Black => [3, 5, 13, 21, 23],
                        }
                        .contains(pos)
                    })
                    .collect::<Vec<_>>()
                }
                Piece::Elephant => {
                    [
                        ((row + 2, col + 2), (row + 1, col + 1)),
                        ((row + 2, col - 2), (row + 1, col - 1)),
                        ((row - 2, col + 2), (row - 1, col + 1)),
                        ((row - 2, col - 2), (row - 1, col - 1)),
                    ]
                    .into_iter()
                    // 不出界
                    .filter_map(|(dest, eye)| (Pos::from_row_col(dest).zip(Pos::from_row_col(eye))))
                    // 不过河
                    .filter(|(Pos(pos), _)| {
                        match color {
                            Color::Red => [47, 51, 63, 67, 71, 83, 87],
                            Color::Black => [2, 6, 18, 22, 26, 38, 42],
                        }
                        .contains(pos)
                    })
                    // 不塞象眼
                    .filter(|(_, eye)| self.pieces[eye.index()].is_none())
                    .map(|(dest, _)| dest)
                    .collect::<Vec<_>>()
                }
                Piece::Horse => {
                    [
                        ((row + 2, col + 1), (row + 1, col)),
                        ((row + 2, col - 1), (row + 1, col)),
                        ((row + 1, col + 2), (row, col + 1)),
                        ((row + 1, col - 2), (row, col - 1)),
                        ((row - 1, col + 2), (row, col + 1)),
                        ((row - 1, col - 2), (row, col - 1)),
                        ((row - 2, col + 1), (row - 1, col)),
                        ((row - 2, col - 1), (row - 1, col)),
                    ]
                    .into_iter()
                    // 不出界
                    .filter_map(|(dest, leg)| (Pos::from_row_col(dest).zip(Pos::from_row_col(leg))))
                    // 不憋腿
                    .filter(|(_, leg)| self.pieces[leg.index()].is_none())
                    .map(|(dest, _)| dest)
                    .collect::<Vec<_>>()
                }
                Piece::Rook => [
                    (0..=row - 1).rev().map(|r| (r, col)).collect::<Vec<_>>(),
                    (row + 1..=9).map(|r| (r, col)).collect::<Vec<_>>(),
                    (0..=col - 1).rev().map(|c| (row, c)).collect::<Vec<_>>(),
                    (col + 1..=8).map(|c| (row, c)).collect::<Vec<_>>(),
                ]
                .into_iter()
                .map(|dests| {
                    dests
                        .into_iter()
                        .filter_map(|rc| Pos::from_row_col(rc))
                        .collect::<Vec<_>>()
                })
                .map(|mut dests| {
                    if let Some(index) = dests
                        .iter()
                        .position(|pos| self.pieces[pos.index()].is_some())
                    {
                        dests.truncate(index + 1)
                    }
                    dests
                })
                .flatten()
                .collect::<Vec<_>>(),
                Piece::Cannon => [
                    (0..=row - 1).rev().map(|r| (r, col)).collect::<Vec<_>>(),
                    (row + 1..=9).map(|r| (r, col)).collect::<Vec<_>>(),
                    (0..=col - 1).rev().map(|c| (row, c)).collect::<Vec<_>>(),
                    (col + 1..=8).map(|c| (row, c)).collect::<Vec<_>>(),
                ]
                .into_iter()
                .map(|dests| {
                    dests
                        .into_iter()
                        .filter_map(|rc| Pos::from_row_col(rc))
                        .collect::<Vec<_>>()
                })
                .map(|mut dests| {
                    // 查找炮架
                    if let Some(index) = dests
                        .iter()
                        .position(|pos| self.pieces[pos.index()].is_some())
                    {
                        let targets = dests.split_off(index);
                        if let Some(target) = targets
                            .into_iter()
                            .skip(1)
                            .find(|pos| self.pieces[pos.index()].is_some())
                        {
                            dests.push(target);
                        }
                    }
                    dests
                })
                .flatten()
                .collect::<Vec<_>>(),
                Piece::Pawn => {
                    let mut dests = vec![
                        // 只前进不后退
                        match color {
                            Color::Red => (row - 1, col),
                            Color::Black => (row + 1, col),
                        },
                    ];
                    // 过河后可平移
                    if match color {
                        Color::Red => row < 5,
                        Color::Black => row > 4,
                    } {
                        dests.push((row, col + 1));
                        dests.push((row, col - 1));
                    }
                    dests
                        .into_iter()
                        // 不出界
                        .filter_map(Pos::from_row_col)
                        .collect::<Vec<_>>()
                }
            }
            .into_iter()
            // 不吃己方棋子
            .filter(|pos| {
                self.pieces[pos.index()]
                    .map(|(target_color, _)| target_color != color)
                    .unwrap_or(true)
            }) {
                possible.push(destination);
            }
        }
        possible
    }

    // 是否出现将帅见面
    pub fn is_general_meet(&self) -> bool {
        let (general_rows, general_cols): (Vec<_>, Vec<_>) = self
            .iter()
            .filter_map(|(pos, _, piece)| {
                if piece == Piece::General {
                    Some(pos.row_col())
                } else {
                    None
                }
            })
            .unzip();
        general_cols.len() == 2
            && general_cols[0] == general_cols[1]
            && range_between(&general_rows)
                .filter_map(|row| Pos::from_row_col((row, general_cols[0])))
                .all(|pos| self.pieces[pos.index()].is_none())
    }

    // 是否叫将
    pub fn check_killing(&self, player: Color) -> Option<Pos> {
        let opponent = player.opponent();
        for (pos, color, _) in self.iter() {
            if color == opponent {
                for dest in self.possible_moves(pos) {
                    if let Some((_, piece)) = self.get_piece(dest) {
                        if piece == Piece::General {
                            return Some(dest);
                        }
                    }
                }
            }
        }
        return None;
    }

    // 是否困毙
    pub fn is_trapped(&self, player: Color) -> bool {
        for (pos, color, _) in self.iter() {
            if color == player {
                if self.possible_moves(pos).len() > 0 {
                    return false;
                }
            }
        }
        return true;
    }

    // 是否绝杀
    pub fn is_killed(&self, player: Color) -> bool {
        let mut test = self.clone();
        for (pos, color, _) in self.iter() {
            if color == player {
                for dest in self.possible_moves(pos) {
                    let eaten_piece = test.apply_move(pos, dest);
                    if test.check_killing(player).is_none() && !test.is_general_meet() {
                        return false;
                    } else {
                        test.revert_move(pos, dest, eaten_piece);
                    }
                }
            }
        }
        return true;
    }

    // 尝试走棋
    pub fn try_move(&mut self, turn: Color, from: Pos, to: Pos) -> (bool, Option<(Color, Piece)>) {
        if let Some((player, _)) = self.get_piece(from) {
            if player == turn && self.possible_moves(from).contains(&to) {
                let eaten_piece = self.apply_move(from, to);
                if self.check_killing(player).is_some() {
                    // 走棋后导致被对方将军
                    self.revert_move(from, to, eaten_piece);
                    return (false, None);
                }
                if self.is_general_meet() {
                    // 走棋后出现将帅见面
                    self.revert_move(from, to, eaten_piece);
                    return (false, None);
                }
                return (true, eaten_piece);
            }
        }
        return (false, None);
    }
}

fn range_between(rows: &[i8]) -> std::ops::Range<i8> {
    if rows[0] < rows[1] {
        (rows[0] + 1)..rows[1]
    } else {
        (rows[1] + 1)..rows[0]
    }
}
